// Taller 06-07-2011.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <list>
#include "math.h"

using namespace std;

class BmpHandler {
private:
	char* Buffer4Bytes;
	char* Buffer3Bytes;
	char* Buffer2Bytes;

public:

BmpHandler::BmpHandler() {
	this->Buffer4Bytes = new char [4];
	this->Buffer3Bytes = new char [3];
	this->Buffer2Bytes = new char [2];
}

BmpHandler::~BmpHandler() {
	delete this->Buffer4Bytes;
	delete this->Buffer3Bytes;
	delete this->Buffer2Bytes;
}
	
char* BmpHandler::getBuffer2Bytes() {
	return this->Buffer2Bytes;
}

char* BmpHandler::getBuffer3Bytes() {
	return this->Buffer3Bytes;
}

char* BmpHandler::getBuffer4Bytes() {
	return this->Buffer4Bytes;
}

char* BmpHandler::escribir2Bytes(unsigned int numero) {

	memcpy(this->Buffer2Bytes, &numero, 2);
	return this->Buffer2Bytes;
}

char* BmpHandler::escribir3Bytes(unsigned int numero) {

	memcpy(this->Buffer3Bytes, &numero, 3);
	return this->Buffer3Bytes;
}

char* BmpHandler::escribir4Bytes(unsigned int numero) {

	memcpy(this->Buffer4Bytes, &numero, sizeof(unsigned int));
	return this->Buffer4Bytes;
}

unsigned int BmpHandler::leer2Bytes(fstream* bmp) {

	unsigned int byte1 = bmp->get();
	unsigned int byte2 = bmp->get();

	return byte1 + byte2 * 256;
}

unsigned int BmpHandler::leer3Bytes (fstream* bmp) {
	unsigned int byte1 = bmp->get();
	unsigned int byte2 = bmp->get();
	unsigned int byte3 = bmp->get();

	return byte1 + byte2 * 256 + byte3 * 256 * 256;
}

unsigned int BmpHandler::leer4Bytes(fstream* bmp) {

	unsigned int byte1 = bmp->get();
	unsigned int byte2 = bmp->get();
	unsigned int byte3 = bmp->get();
	unsigned int byte4 = bmp->get();

	return byte1 + byte2 * 256 + byte3 * 256 * 256 + byte4 * 256 * 256 * 256;
}

void BmpHandler::getInformacion(string path) {
	fstream bmp;
	bmp.open(path.c_str(), ios::in | ios::out | ios::binary);

	cout << "El archivo: " << path << endl;
	unsigned int IdBmp;
	unsigned int tamBmp;
	IdBmp = this->leer2Bytes(&bmp);
	this->escribir2Bytes(IdBmp);
	cout << "El identificador: " << this->getBuffer2Bytes() << endl;
	tamBmp = this->leer4Bytes(&bmp);
	cout << "Tamanio de la imagen: " << tamBmp << " Bytes" << endl;
	this->leer4Bytes(&bmp);
	unsigned int tamCabecera = this->leer4Bytes(&bmp);
	cout << "Tamanio de la cabecera: " << tamCabecera << " Bytes" << endl;
	unsigned int header = this->leer4Bytes(&bmp);
	cout << "Header size: " << header << " Bytes" << endl;
	unsigned int ancho = this->leer4Bytes(&bmp);
	cout << "Ancho de la imagen: " << ancho << " en Pixeles" << endl;
	unsigned int alto = this->leer4Bytes(&bmp);
	cout << "Alto de la imagen: " << alto << " en Pixeles" << endl;
	this->leer2Bytes(&bmp);
	unsigned int bpp = this->leer2Bytes(&bmp);
	cout << "Bits por Pixel: " << bpp << endl;
	unsigned int comp = this->leer4Bytes(&bmp);
	if (comp == 0) {
		cout << "Compression: " << comp << " (Sin comprimir)" << endl;
	}
	unsigned int tamDatos = this->leer4Bytes(&bmp);
	cout << "Tamanio de datos: " << tamDatos << " en Bytes" << endl;
	unsigned int Hres = this->leer4Bytes(&bmp);
	cout << "Horizontal Resolution: " << Hres << " Pixel/Meter" << endl;
	unsigned int Vres = this->leer4Bytes(&bmp);
	cout << "Vertical Resolution: " << Vres << " Pixel/Meter" << endl;
	unsigned int usedColors = this->leer4Bytes(&bmp);
	cout << "Used colors: " << usedColors << " (0: None)" << endl;
	unsigned int impColors = this->leer4Bytes(&bmp);
	cout << "Important colors: " << impColors
			<< " (0: All colors are important)" << endl;
}
};

void getRGB (unsigned int pixel, unsigned int& r, unsigned int& g, unsigned int& b) {
	r = pixel / (256 * 256);
	pixel -= r * (256 * 256);
	g = pixel / 256;
	pixel -= g * 256;
	b = pixel;
}

unsigned int getColor ( unsigned int r, unsigned int g, unsigned b ) {
	unsigned int result = 0;
	result += r * (256 * 256);
	result += g * 256;
	result += b;
	return result;
}

bool enRango (const unsigned int x, const unsigned int y, const unsigned int ancho, const unsigned int alto) {
	if ( x >= 0 && x <= ancho-1 && y >= 0 && y <= alto-1 ) {
		return true;
	} else {
		return false;
	}
}

unsigned int getPosicion (const unsigned int x, const unsigned int y, const unsigned int ancho) {
	return ( x + ( y * ancho ) );
}

unsigned int getCantTotal (map<unsigned int, unsigned int> lista) {
	unsigned int value = 0;
    map<unsigned int, unsigned int>::iterator itLista;
    for (itLista = lista.begin(); itLista != lista.end(); itLista++) {
		value += itLista->second;
    }
    return value;
}

void rellenar (unsigned int* arrayImag, const unsigned int x, const unsigned int y, const unsigned int ancho, 
			   const unsigned int alto, const unsigned int color) {
	if ( enRango (x, y, ancho, alto) ) {
		unsigned int pos = getPosicion (x, y, ancho);
		for (unsigned int i = 0 ; i < pos ; i++) {
			*arrayImag++;
		}
		*arrayImag = color;
	}
}

void mapAgregaColor (map<unsigned int, unsigned int> *listaColores, unsigned int colorValue) {
        map<unsigned int, unsigned int>::iterator itLista;
        itLista = listaColores->find(colorValue);

        if (itLista != listaColores->end())
        {
                itLista->second++;
        }else{
                listaColores->insert(pair<unsigned int, unsigned int>(colorValue, 1));
        }
}

void mostrarPorcentajes (map<unsigned int, unsigned int> lista) {

	unsigned int total = getCantTotal (lista);
	unsigned int componente;
	unsigned int cant;
	unsigned int auxPorc = 0;
	map<unsigned int, unsigned int>::iterator itLista;
	for (itLista = lista.begin(); itLista != lista.end(); itLista++) {
		componente = itLista->first;
        cant = itLista->second;
		float porc = 0.0;
		porc += cant;
		porc /= total;
		porc *= 100;
		float aux = porc;
		unsigned int porcI = porc;
		aux -= porcI;
		if (aux >= 0.5) {
			porcI++;
		}
		auxPorc += porcI;
		while (auxPorc > 100) {
			auxPorc--;
			porcI--;
			//cout << "Aux Porcentaje: " << auxPorc << endl;
		}
		
		
		cout << "Componente:= " << componente << endl;
		cout << "Porcentaje:= " << porcI << endl;
    }
}

void blanquearImagen (unsigned int* vector, unsigned int ancho, unsigned int alto) {
	unsigned int cont = 0;
	unsigned int blanco = getColor(255,255,255);
	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			vector[cont] = blanco;
			cont++;
		}
	}
}

unsigned int* getImagenBlanco (unsigned int ancho, unsigned int alto) {
	unsigned int cont = 0;
	unsigned int blanco = getColor(255,255,255);
	unsigned int pixel;
	unsigned int* imagen = new unsigned int [ancho * alto];

	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			imagen[cont] = blanco;
			cont++;
		}
	}
	
	return imagen;
}

void mostrarImagen (unsigned int* arreglo, unsigned int ancho, unsigned int alto) {
	unsigned int cont = 0;
	for (unsigned int y = 0 ; y < (alto/2) ; y++) {
		for (unsigned int x = 10 ; x < 14 ; x++) {
			cout << "Color[" << x << "," << y << "]: " << arreglo[cont] << " ";
			cont++;
		}
		cout << endl;
	}
}

void procesarImagenR (unsigned int ancho, unsigned int alto, 
					  map<unsigned int,unsigned int> lista, FILE* bmpDest) {
	BmpHandler* bmpHandler = new BmpHandler();
	unsigned int* pixeles = new unsigned int [alto * ancho];
	blanquearImagen (pixeles, ancho, alto);
	unsigned int total = getCantTotal (lista);
	unsigned int componente;
	unsigned int cant;
	unsigned int color;
	unsigned int auxPorc = 0;
	map<unsigned int, unsigned int>::iterator itLista;
	for (itLista = lista.begin(); itLista != lista.end(); itLista++) {
		componente = itLista->first;
        cant = itLista->second;
		float porc = 0.0;
		porc += cant;
		porc /= total;
		porc *= 100;
		float aux = porc;
		unsigned int porcI = porc;
		aux -= porcI;
		if (aux >= 0.5) {
			porcI++;
		}
		auxPorc += porcI;
		while (auxPorc > 100) {
			auxPorc--;
			porcI--;
		}
		color = getColor(componente, 0, 0);
		//cout << "Color: " << color << endl;
		for (unsigned int i = 0 ; i < porcI ; i++) {
			rellenar (pixeles, componente, i, ancho, alto, color);			
		}
		
		
    }
	unsigned int cont = 0;
	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			unsigned int color = pixeles[cont];
			fwrite (&color, 3, 1, bmpDest);
			cont++;
		}
		for (unsigned int p = 0 ; p < (ancho % 2) ; p++) {
			char aux = '0';
			fwrite (&aux, 1, 1, bmpDest);
		}
	}
	
	delete bmpHandler;
	delete[] pixeles;
}

void procesarImagenG (unsigned int ancho, unsigned int alto, 
					  map<unsigned int,unsigned int> lista, FILE* bmpDest) {
	BmpHandler* bmpHandler = new BmpHandler();
	unsigned int* pixeles = new unsigned int [alto * ancho];
	blanquearImagen (pixeles, ancho, alto);
	unsigned int total = getCantTotal (lista);
	unsigned int componente;
	unsigned int cant;
	unsigned int color;
	unsigned int auxPorc = 0;
	map<unsigned int, unsigned int>::iterator itLista;
	for (itLista = lista.begin(); itLista != lista.end(); itLista++) {
		componente = itLista->first;
        cant = itLista->second;
		float porc = 0.0;
		porc += cant;
		porc /= total;
		porc *= 100;
		float aux = porc;
		unsigned int porcI = porc;
		aux -= porcI;
		if (aux >= 0.5) {
			porcI++;
		}
		auxPorc += porcI;
		while (auxPorc > 100) {
			auxPorc--;
			porcI--;
		}
		color = getColor(0, componente, 0);
		//cout << "Color: " << color << endl;
		for (unsigned int i = 0 ; i < porcI ; i++) {
			rellenar (pixeles, componente, i, ancho, alto, color);			
		}
		
		
    }

	unsigned int cont = 0;
	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			unsigned int color = pixeles[cont];
			fwrite (&color, 3, 1, bmpDest);
			cont++;
		}
		for (unsigned int p = 0 ; p < (ancho % 2) ; p++) {
			char aux = '0';
			fwrite (&aux, 1, 1, bmpDest);
		}
	}
	
	delete bmpHandler;
	delete[] pixeles;
}

void procesarImagenB (unsigned int ancho, unsigned int alto, 
					  map<unsigned int,unsigned int> lista, FILE* bmpDest) {
	BmpHandler* bmpHandler = new BmpHandler();
	unsigned int* pixeles = new unsigned int [alto * ancho];
	blanquearImagen (pixeles, ancho, alto);
	unsigned int total = getCantTotal (lista);
	unsigned int componente;
	unsigned int cant;
	unsigned int color;
	unsigned int auxPorc = 0;
	map<unsigned int, unsigned int>::iterator itLista;
	for (itLista = lista.begin(); itLista != lista.end(); itLista++) {
		componente = itLista->first;
        cant = itLista->second;
		float porc = 0.0;
		porc += cant;
		porc /= total;
		porc *= 100;
		float aux = porc;
		unsigned int porcI = porc;
		aux -= porcI;
		if (aux >= 0.5) {
			porcI++;
		}
		auxPorc += porcI;
		while (auxPorc > 100) {
			auxPorc--;
			porcI--;
		}
		color = getColor(0, 0, componente);
		//cout << "Color: " << color << endl;
		for (unsigned int i = 0 ; i < porcI ; i++) {
			rellenar (pixeles, componente, i, ancho, alto, color);			
		}
		
		
    }

	unsigned int cont = 0;
	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			unsigned int color = pixeles[cont];
			fwrite (&color, 3, 1, bmpDest);
			cont++;
		}
		for (unsigned int p = 0 ; p < (ancho % 2) ; p++) {
			char aux = '0';
			fwrite (&aux, 1, 1, bmpDest);
		}
	}
	
	delete bmpHandler;
	delete[] pixeles;
}

void procesarImagen (unsigned int* pixeles, unsigned int ancho, unsigned int alto, 
					 map<unsigned int, unsigned int>* listaR,
					 map<unsigned int, unsigned int>* listaG, 
					 map<unsigned int, unsigned int>* listaB) {
	unsigned int cont = 0;
	unsigned int blanco = getColor(255,255,255);
	unsigned int pixel;

	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			pixel = pixeles[cont];
			if (pixel != blanco) {
				// Agrego los componentes
				unsigned int compR;
				unsigned int compG;
				unsigned int compB;
				getRGB (pixel, compR, compG, compB);
				// Agrego el componente rojo
				mapAgregaColor (listaR, compR);
				// Agrego el componente verde
				mapAgregaColor (listaG, compG);
				// Agrego el componente verde
				mapAgregaColor (listaB, compB);
			}
			cont++;
		}
	}

}

void procesarBmp (string bmpOrigen, string bmpDestR, 
				  string bmpDestG, string bmpDestB) {
	FILE* bmpOrig;
	FILE* bmpR;
	FILE* bmpG;
	FILE* bmpB;
	bmpOrig = fopen (bmpOrigen.c_str(), "rb");
	bmpR = fopen (bmpDestR.c_str(), "wb");
	bmpG = fopen (bmpDestG.c_str(), "wb");
	bmpB = fopen (bmpDestB.c_str(), "wb");
	
	
	bool esBmp = false;
	BmpHandler* bmpHandler = new BmpHandler();

	if ( fgetc(bmpOrig) == 'B' && fgetc(bmpOrig) == 'M') {
		esBmp = true;
	}

	if (esBmp) {

		unsigned int tamCabecera;
		unsigned int tamArch;
		unsigned int ancho;
		unsigned int alto;

		// Cargo los datos importantes del bmp original
		fread (&tamArch, 4, 1, bmpOrig);
		fseek(bmpOrig, 10, SEEK_SET);
		fread (&tamCabecera, 4, 1, bmpOrig);
		fseek (bmpOrig, 18, SEEK_SET);
		fread (&ancho, 4, 1, bmpOrig);
		fread (&alto, 4, 1, bmpOrig);

		// Copio la cabeceras en los 3 bmp destino
		char* cabecera = new char [tamCabecera];
		fseek (bmpOrig, 0, SEEK_SET);
		fread (cabecera, 54, 1, bmpOrig);
		fwrite (cabecera, tamCabecera, 1, bmpR);
		fwrite (cabecera, tamCabecera, 1, bmpG);
		fwrite (cabecera, tamCabecera, 1, bmpB);
		
		// Cargo el pixel array en memoria
		unsigned int* vectOrig = new unsigned int [ancho * alto];
		unsigned int color;
		unsigned int cont = 0;
		for (unsigned int y = 0 ; y < alto ; y++) {
			for (unsigned int x = 0 ; x < ancho ; x++) {
				fread (&color, 3, 1, bmpOrig);
				vectOrig[cont] = color;
				cont++;
			}
			for (unsigned int p = 0 ; p < (ancho % 2) ; p++) {
				char aux;
				fread (&aux, 1, 1, bmpOrig);
			}
		}

		map<unsigned int,unsigned int>* listaR = new map<unsigned int,unsigned int>();
		map<unsigned int,unsigned int>* listaG = new map<unsigned int,unsigned int>();
		map<unsigned int,unsigned int>* listaB = new map<unsigned int,unsigned int>();

		procesarImagen (vectOrig, ancho, alto, listaR, listaG, listaB);
		


		cout << "--- Mostrando porcentajes de Rojo ---" << endl;
		mostrarPorcentajes (*listaR);
		cout << "--- Mostrando porcentajes de Verde ---" << endl;
		mostrarPorcentajes (*listaG);
		cout << "--- Mostrando porcentajes de Azul ---" << endl;
		mostrarPorcentajes (*listaB);


		procesarImagenR (ancho, alto, *listaR, bmpR);
		procesarImagenG (ancho, alto, *listaG, bmpG);
		procesarImagenB (ancho, alto, *listaB, bmpB);


		fclose (bmpR);
		fclose (bmpG);
		fclose (bmpB);

		
		 
		
		delete[] cabecera;
		delete[] vectOrig;
		delete listaR;
		delete listaG;
		delete listaB;

	}

	fclose (bmpOrig);
	delete bmpHandler;

}

void testRellenar (string bmpDestino) {
	BmpHandler* bmpHandler = new BmpHandler();
	fstream bmpDest;
	bmpDest.open (bmpDestino.c_str(), fstream::out, fstream::binary);
	unsigned int ancho = 4;
	unsigned int alto = 4;
	char aux;
	aux = 'B';
	bmpDest.write (&aux, 1);
	aux = 'M';
	bmpDest.write (&aux, 1);
	bmpDest.write (bmpHandler->escribir4Bytes (102), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (0), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (54), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (40), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (ancho), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (alto), 4);
	bmpDest.write (bmpHandler->escribir2Bytes (0), 2);
	bmpDest.write (bmpHandler->escribir2Bytes (24), 2);
	bmpDest.write (bmpHandler->escribir4Bytes (0), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (48), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (0), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (0), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (0), 4);
	bmpDest.write (bmpHandler->escribir4Bytes (0), 4);
	unsigned int* vector = new unsigned int [ancho * alto];
	unsigned int color = getColor (10, 0, 0);
	rellenar (vector, 0, 0, ancho, alto, color);
	rellenar (vector, 0, 1, ancho, alto, color);
	rellenar (vector, 0, 2, ancho, alto, color);
	rellenar (vector, 0, 3, ancho, alto, color);
	color = getColor (255,255,255);
	rellenar (vector, 1, 0, ancho, alto, color);
	rellenar (vector, 1, 1, ancho, alto, color);
	rellenar (vector, 1, 2, ancho, alto, color);
	rellenar (vector, 1, 3, ancho, alto, color);

	unsigned int cont = 0;
	for (unsigned int y = 0 ; y < alto ; y++) {
		for (unsigned int x = 0 ; x < ancho ; x++) {
			unsigned int color = vector[cont];
			cont++;
			bmpDest.write (bmpHandler->escribir3Bytes (color), 3);
		}
		for (unsigned int p = 0 ; p < (ancho % 2) ; p++) {
			char aux = '0';
			bmpDest.write (&aux, sizeof (char));
		}
	}

	delete[] vector;
	delete bmpHandler;
}


int _tmain(int argc, _TCHAR* argv[]) {
	procesarBmp ("origen.bmp", "r_87915.bmp", "g_87915.bmp", "b_87915.bmp");
	BmpHandler* bmpHandler = new BmpHandler();
	bmpHandler->getInformacion ("r_87915.bmp");
	//testRellenar ("testRellenar.bmp");

	//delete bmpHandler;
	system ("pause");
	return 0;
}

